package com.infolab.aims.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.infolab.aims.common.NotEqualQueryHelper;
import com.infolab.aims.common.PaginationHelper;
import com.infolab.aims.common.QueryHelper;
import com.infolab.aims.common.bean.SearchCriteria;
import com.infolab.aims.common.bean.SearchResult;
import com.infolab.aims.common.exception.DataStoreException;
import com.infolab.aims.domain.SystemRole;
import com.infolab.aims.domain.SystemUser;
import com.infolab.aims.service.intf.SystemUserService;


@Service
@Transactional
public class SystemUserServiceImpl implements SystemUserService{
	
	Log logger = LogFactory.getLog(getClass());
	@PersistenceContext
	private EntityManager em;
	
	public void createUser(SystemUser systemUser)
	{
		SystemUser newSystemUser = new SystemUser();
		if(systemUser.getPassword()!=null)
			systemUser.setPassword(DigestUtils.md5Hex(systemUser.getPassword().getBytes()));
		
		if(systemUser.getDepartment()==null || systemUser.getDepartment().getDepartmentId()==null)
			systemUser.setDepartment(null);
		
		if(systemUser.getSection()==null || systemUser.getSection().getSectionId()==null)
			systemUser.setSection(null);
		
		if(StringUtils.isNotEmpty(systemUser.getNric()))
		{
			SearchCriteria searchCriteria = new SearchCriteria();
			searchCriteria.addSearchCriteria("nric", systemUser.getNric());
			SystemUser tmp = findUser(searchCriteria);
			if(tmp!=null)
				throw new DataStoreException(true,"nric");
		}
		
		systemUser.setModifiedBy(systemUser.getCreatedBy());
		systemUser.setDateCreated(new Date());
		systemUser.setDateModified(new Date());
		
		BeanUtils.copyProperties(systemUser,newSystemUser);
		
		em.persist(newSystemUser);
		
	}

	public void updateUser(SystemUser systemUser) {
		
		
		SystemUser curSystemUser = em.find(SystemUser.class, systemUser.getUserName());
		if(systemUser.getDepartment()==null || systemUser.getDepartment().getDepartmentId()==null)
			systemUser.setDepartment(null);
			
		if(systemUser.getSection()==null || systemUser.getSection().getSectionId()==null)
			systemUser.setSection(null);
		
		if(StringUtils.isNotEmpty(systemUser.getNric()))
		{
			SearchCriteria searchCriteria = new SearchCriteria();
			searchCriteria.addSearchCriteria("nric", systemUser.getNric());
			
			searchCriteria.addSearchCriteria("userName", new NotEqualQueryHelper("userName", systemUser.getUserName()));
			
			SystemUser tmp = findUser(searchCriteria);
			if(tmp!=null )
				throw new DataStoreException(true,"nric");
		}
		
		curSystemUser.setDepartment(systemUser.getDepartment());
		curSystemUser.setSection(systemUser.getSection());
		curSystemUser.setContactInfo(systemUser.getContactInfo());
		curSystemUser.setUserName(systemUser.getUserName());
		curSystemUser.setUserType(systemUser.getUserType());
		curSystemUser.setSuperAdmin(systemUser.isSuperAdmin());
		curSystemUser.setPersonName(systemUser.getPersonName());
		curSystemUser.setNric(systemUser.getNric());
		curSystemUser.setActive(systemUser.isActive());
		curSystemUser.setModifiedBy(systemUser.getModifiedBy());
		curSystemUser.setDateModified(new Date());
		
		em.merge(curSystemUser);
		if(curSystemUser.isSuperAdmin())
		{
			List updatedSystemRoles = new ArrayList<SystemRole>();
			curSystemUser.setSystemRoles(updatedSystemRoles);
			updateUserRoles(curSystemUser);
		}
	}
	
	public void updateUserPassword(String userName,String newPassword) {
		
		SystemUser curSystemUser = em.find(SystemUser.class, userName);
		
		curSystemUser.setPassword(DigestUtils.md5Hex(newPassword.getBytes()));
		curSystemUser.setDateModified(new Date());
		em.merge(curSystemUser);
	}

	public void updateUserRoles(SystemUser systemUser) {
		SystemUser curSystemUser = em.find(SystemUser.class, systemUser.getUserName());
		List<SystemRole> systemRoles = systemUser.getSystemRoles();
		List<SystemRole> newSystemRoles = new ArrayList<SystemRole>();
		if(systemUser.getDepartment()==null || systemUser.getDepartment().getDepartmentId()==null)
			systemUser.setDepartment(null);
		
		if(systemUser.getSection()==null || systemUser.getSection().getSectionId()==null)
			systemUser.setSection(null);
		
		curSystemUser.setDepartment(systemUser.getDepartment());
		curSystemUser.setSection(systemUser.getSection());
		curSystemUser.setModifiedBy(systemUser.getModifiedBy());
		curSystemUser.setDateModified(new Date());
		if(systemRoles!=null)
		{			
			if(systemRoles.size()>0){
				curSystemUser.setSuperAdmin(false);
			}
			
			for(int i=0;i<systemRoles.size();i++)
			{
				SystemRole tmp = systemRoles.get(i);
				Integer tmpKey = tmp.getSystemRoleId();
				tmp = em.getReference(SystemRole.class, tmpKey);
				newSystemRoles.add(tmp);
			}
		}
		curSystemUser.setSystemRoles(newSystemRoles);
		
		em.merge(curSystemUser);
	}

	public SystemUser getUser(String userName) {
		// TODO Auto-generated method stub
		return em.find(SystemUser.class, userName);
	}

	public SystemUser findUser(SearchCriteria searchCriteria) {
		List<SystemUser> systemUsers = findUsers(searchCriteria);
		if(systemUsers!=null && systemUsers.size()>=1)
			return systemUsers.get(0);
		
		return null;
	}
	
	public List<SystemUser> findUsers(SearchCriteria searchCriteria) {
		
		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<SystemUser> cq = cb.createQuery(SystemUser.class);
		Root<SystemUser> root = cq.from(SystemUser.class);
		List<Predicate> criteria = new ArrayList<Predicate>();
		if (searchCriteria.getSearchCriteria("nric")!= null) {
			ParameterExpression<String> p =
				cb.parameter(String.class, "nric");
			criteria.add(cb.equal(cb.upper(root.<String>get("nric")), cb.upper( p)));
		}
		if (searchCriteria.getSearchCriteria("likeNric")!= null) {
			ParameterExpression<String> p =
				cb.parameter(String.class, "likeNric");
			criteria.add(cb.like(cb.upper(root.<String>get("nric")), cb.upper( p)));
		}
		
		if (searchCriteria.getSearchCriteria("userName")!= null) {
			
			Object obj = searchCriteria.getSearchCriteria("userName");
			if(obj instanceof NotEqualQueryHelper)
			{	
				ParameterExpression<String> p =
					cb.parameter(String.class, "userName");
				criteria.add(cb.notEqual(root.get("userName"), p));
			}
			else
			{
				ParameterExpression<String> p =
					cb.parameter(String.class, "userName");
				criteria.add(cb.equal(root.get("userName"), p));
			}
		}

		if (searchCriteria.getSearchCriteria("userType")!= null) {
			ParameterExpression<String> p =
				cb.parameter(String.class, "userType");
			criteria.add(cb.equal(root.get("userType"),  p));
		}
		cq.select(root);
		if (criteria.size() == 0) {
			} else if (criteria.size() == 1) {
			cq.where(criteria.get(0));
			} else {
			cq.where(cb.and(criteria.toArray(new Predicate[0])));
		}
		
		if(searchCriteria.getSortType()!=null)
		{
			if(searchCriteria.isAscendingSort())
				cq.orderBy(cb.asc(root.get(searchCriteria.getSortName())));
			else
				cq.orderBy(cb.desc(root.get(searchCriteria.getSortName())));	
		}
		
		Query query = em.createQuery(cq);
		
		if (searchCriteria.getSearchCriteria("nric")!= null) {
			query.setParameter("nric", searchCriteria.getSearchCriteria("nric"));
		}
		if (searchCriteria.getSearchCriteria("likeNric")!= null) {
			query.setParameter("likeNric", "%"+StringUtils.upperCase((String)searchCriteria.getSearchCriteria("likeNric"))+"%");
		}
		
		if (searchCriteria.getSearchCriteria("userName")!= null) {
			
			Object obj = searchCriteria.getSearchCriteria("userName");
			if(obj instanceof NotEqualQueryHelper)
			{	
				query.setParameter("userName", ((NotEqualQueryHelper)obj).getColumnValue());
			}
			else
			{
				query.setParameter("userName", searchCriteria.getSearchCriteria("userName"));
			}
		}

		if (searchCriteria.getSearchCriteria("userType")!= null) {
			query.setParameter("userType", searchCriteria.getSearchCriteria("userType"));
		}
		
		if(searchCriteria.getMaxRow()!=null)
			query.setMaxResults(searchCriteria.getMaxRow());
		
		
		
		List<SystemUser> systemUsers = query.getResultList();
		if(systemUsers==null || systemUsers.size()==0)
			return null;
		return systemUsers;
	}

	public SearchResult searchUser(SearchCriteria searchCriteria) {
		
		if(searchCriteria.getCurPage()==null)
			searchCriteria.setCurPage(1);
		
		//em.createQuery(SystemUser.class);
		UserPaginationHelper helper = new UserPaginationHelper();
		
		return helper.getSearchResultFromQueryHelper(em, searchCriteria);
//		CriteriaBuilder cb = em.getCriteriaBuilder();
//		
//		
//		CriteriaQuery<SystemUser> cq = cb.createQuery(SystemUser.class);
//		CriteriaQuery<Long> cqCount = cb.createQuery(Long.class);
//		Root<SystemUser> root = cq.from(SystemUser.class);
//		List<Predicate> criteria = new ArrayList<Predicate>();
//		
//		for(int i=0;i<Pagination.VALID_SEARCHNAME.length;i++)
//		{
//			String searchValue = searchCriteria.getSearchCriteria(Pagination.VALID_SEARCHNAME[i]);
//			if(StringUtils.isNotEmpty(searchValue))
//			{
//				ParameterExpression<String> p = cb.parameter(String.class, Pagination.VALID_SEARCHNAME[i]);
//					criteria.add(cb.like(root.<String>get(Pagination.VALID_SEARCHNAME[i]), "%"+searchValue+"%"));
//			}
//		}
//		if (criteria.size() == 0) {
//		} else if (criteria.size() == 1) {
//		cq.where(criteria.get(0));
//		cqCount.where(criteria.get(0));
//		} else {
//		cq.where(cb.and(criteria.toArray(new Predicate[0])));
//		cqCount.where(cb.and(criteria.toArray(new Predicate[0])));
//		}
//	
//		
//		cq.select(root);
//		
//		if(StringUtils.isEmpty(searchCriteria.getSortName()))
//			cq.orderBy(cb.asc(root.get(Pagination.DEFAULT_SORTNAME)));
//		else
//		{
//			if(ArrayUtils.contains(Pagination.VALID_SORTNAME, searchCriteria.getSortName()))
//			{
//				if(searchCriteria.isAscendingSort())
//					cq.orderBy(cb.asc(root.get(searchCriteria.getSortName())));
//				else
//					cq.orderBy(cb.desc(root.get(searchCriteria.getSortName())));
//			}
//		}
//		
//		int curPage = searchCriteria.getCurPage();
//		int maxRow = searchCriteria.getMaxRow();
//		int startPosition = (curPage-1)*maxRow;
//		List<SystemUser> systemUsers = em.createQuery(cq).setMaxResults(maxRow).setFirstResult(startPosition).getResultList();
//		
//		root = cqCount.from(SystemUser.class);
//		
//		cqCount.select(cb.count(root));
//		int countRow = em.createQuery(cqCount).getSingleResult().intValue();
//		int maxPage = (int) Math.ceil(countRow*1.0f/maxRow);
//		
//		SearchResult searchResult = new SearchResult();
//		searchResult.setSearchCriteria(searchCriteria);
//		searchResult.setResult(systemUsers);
//		searchResult.setMaxPage(maxPage);
//		searchResult.setResultCount(countRow);
//		if(startPosition==0)
//			searchResult.setRecordNo(1);
//		else
//			searchResult.setRecordNo(startPosition+1);
//		
//		return searchResult;
	}
	
	public static class UserPaginationHelper extends PaginationHelper<SystemUser> {

		@Override
		public String[] getValidColumnName() {
			return new String[]{"userName","personName","nric","userType","department.departmentId"};
		}
		
		@Override
		public String[] getValidSortName() {
			return new String[]{"userName","personName","nric","userType","systemUser.active","department.departmentName","systemUser.modifiedBy","systemUser.dateModified"};
		}

		@Override
		public String getDefaultSortName() {
			// TODO Auto-generated method stub
			return "userName";
		}
		
		@Override
		public QueryHelper getQueryHelper() {
			QueryHelper queryHelper = new QueryHelper();
			queryHelper.select("select new SystemUser(systemUser.userName,systemUser.nric,systemUser.personName,department.departmentName,systemUser.active,systemUser.userType,systemUser.modifiedBy,systemUser.dateModified) from SystemUser systemUser left join systemUser.department department");
			return queryHelper;
		}
		
		
	}
	
	public List<SystemUser> findUsers(){
		return null;
	}
	
}
